// OLED的源文件

#include <types.h>
#include <string.h>
#include "../lib/ls1c_public.h"
#include "../lib/ls1c_i2c.h"
#include "../lib/ls1c_pin.h"
#include "../lib/ls1c_delay.h"
#include "oled_code_tab.h"


// i2c引脚
#define LS1C_I2C_SDA2_GPIO56            (56)
#define LS1C_I2C_SCL2_GPIO57            (57)


// 一次i2c通信过程中control byte的值
#define OLED_CONTROL_TYPE_CMD           (0x00)
#define OLED_CONTROL_TYPE_DATA          (0x40)


// oled的i2c地址
// 通过调整0R电阻，oled的地址可以为0x78和0x7A，默认为0x78
#define OLED_I2C_ADDR                   (0x78 >> 1)     // 这里只需要前7位，最后一位位R/W#


// oled的页数和列数
#define OLED_PAGE_MAX_NUM               (8)
#define OLED_COLUMN_MAX_NUM             (128)


ls1c_i2c_info_t oled_i2c_info = {0};


// 配置oled的i2c
void oled_i2c_config(void)
{
    // I2C2: 引脚CAMDATA6(GPIO56)和CAMDATA7(GPIO57)的第四复用
    pin_set_remap(LS1C_I2C_SDA2_GPIO56, PIN_REMAP_FOURTH);
    pin_set_remap(LS1C_I2C_SCL2_GPIO57, PIN_REMAP_FOURTH);
    oled_i2c_info.clock     = 100*1000;     // 100k
    oled_i2c_info.I2Cx      = LS1C_I2C_2;
    i2c_init(&oled_i2c_info);

    return ;
}


/*
 * 通过i2c向oled写入信息
 * @control 信息的类型（命令 或 数据）
 * @data 具体的信息内容
 */
void oled_i2c_write(u8 control, u8 data)
{
    u8 send_buff[2] = {0};

    send_buff[0] = control;
    send_buff[1] = data;
    
    i2c_send_start_and_addr(&oled_i2c_info, OLED_I2C_ADDR, LS1C_I2C_DIRECTION_WRITE);
    i2c_receive_ack(&oled_i2c_info);
    i2c_send_data(&oled_i2c_info, send_buff, sizeof(send_buff));
    i2c_send_stop(&oled_i2c_info);

    return ;
}


/*
 * 通过i2c向oled写入命令
 * @cmd 待写入的命令
 */
void oled_i2c_write_cmd(u8 cmd)
{
    oled_i2c_write(OLED_CONTROL_TYPE_CMD, cmd);
}


/*
 * 通过i2c向oled写入数据
 * @data 待写入的数据
 */
void oled_i2c_write_data(u8 data)
{
    oled_i2c_write(OLED_CONTROL_TYPE_DATA, data);
}


// 初始化oled
void oled_init(void)
{
    // 配置i2c
    oled_i2c_config();
    delay_ms(100);                  // 这里需要延迟一会

    oled_i2c_write_cmd(0xAE);       // display off

    oled_i2c_write_cmd(0x20);       // set memory addressing mode
    oled_i2c_write_cmd(0x10);       // 0x10: page address mode
    oled_i2c_write_cmd(0xb0);       // set page start address for page addressing mode
    oled_i2c_write_cmd(0xc8);       // set COM output scan direction
    oled_i2c_write_cmd(0x00);       // set low column address
    oled_i2c_write_cmd(0x10);       // set high column address
    oled_i2c_write_cmd(0x40);       // set start line address
    
    oled_i2c_write_cmd(0x81);       // set contrast control register
    oled_i2c_write_cmd(0xff);       // 亮度调节 0x00~0xFF
    
    oled_i2c_write_cmd(0xa1);       // set segment remap 0 to 127
    oled_i2c_write_cmd(0xa6);       // set normal display
    oled_i2c_write_cmd(0xa8);       // set multiplex ratio (1 to 64)
    oled_i2c_write_cmd(0x3f);

    oled_i2c_write_cmd(0xa4);       // 0xa4: output follows RAM content
    oled_i2c_write_cmd(0xd3);       // set display offset
    oled_i2c_write_cmd(0x00);       // not offset
    oled_i2c_write_cmd(0xd5);       // set display clock divide ratio/oscillator frequency
    oled_i2c_write_cmd(0xf0);       // set divide ratio
    oled_i2c_write_cmd(0xd9);       // set pre-charge period
    oled_i2c_write_cmd(0x22);

    oled_i2c_write_cmd(0xda);       // set com pins hardware configuration
    oled_i2c_write_cmd(0x12);
    oled_i2c_write_cmd(0xdb);       // set vcomh
    oled_i2c_write_cmd(0x20);       // 0x20, 0.77xVcc
    oled_i2c_write_cmd(0x8d);       // set DC-DC enable
    oled_i2c_write_cmd(0x14);
    oled_i2c_write_cmd(0xaf);       // turn on oled panel

    return ;
}


/*
 * 设置从指定坐标开始显示
 * @x_column 坐标x，列地址
 * @y_page   坐标y，页地址
 */
void oled_set_pos(u8 x_column, u8 y_page)
{
    // 设置列地址
    oled_i2c_write_cmd(x_column & 0x0f);                    // 设置低4位
    oled_i2c_write_cmd(((x_column & 0xf0) >> 4) | 0x10);    // 设置高4位

    // 设置页地址
    oled_i2c_write_cmd(0xb0 + y_page);
}


/*
 * 全屏填充
 * @data 填充的数据
 */
void oled_fill(u8 data)
{
    u8 page = 0;
    u8 column = 0;

    // 依次填充每一个page
    for (page=0; page<OLED_PAGE_MAX_NUM; page++)
    {
        // 设置每页的起始坐标
        oled_set_pos(0, page);
        
        // 依次填充每列(column)
        for (column=0; column<OLED_COLUMN_MAX_NUM; column++)
        {
            oled_i2c_write_data(data);
        }
    }

    return ;
}


/*
 * 在oled的指定位置显示指定的字符
 * @x_column 坐标x，列地址
 * @y_page    坐标y，页地址
 * @ch 待显示的字符
 */
void oled_show_en_num_char(u8 x_column, u8 y_page, s8 ch)
{
    u8 col = 0;

    // 经过分析，8x16字模中，高16，占两个page
    // 显示第一个page
    oled_set_pos(x_column, y_page);                 // 设置起始地址
    for (col=0; col<8; col++)                       // 依次显示每列，共8列（8x16的点阵，共8列）
    {
        oled_i2c_write_data(F8x16_ASCII[ch][col]);
    }

    // 显示第二个page
    oled_set_pos(x_column, y_page+1);               // 设置起始地址
    for (col=0; col<8; col++)
    {
        oled_i2c_write_data(F8x16_ASCII[ch][8+col]);
    }

    return ;
}


/*
 * 在oled上显示仅包含英文和数字的字符串
 * @x_column 字符串在oled上的起始坐标x，列地址
 * @y_page 字符串在oled上的起始坐标y，页地址
 * @en_num_str 待显示的字符串
 */
void oled_show_en_num_str(u8 x_column, u8 y_page, char *en_num_str)
{
    u8 len = strlen(en_num_str);
    s8 ch = '\0';
    
    // 检查入参
    if (OLED_COLUMN_MAX_NUM - 1 < x_column)
        x_column = OLED_COLUMN_MAX_NUM - 1;         // 列地址不能超过最大值
    if (OLED_PAGE_MAX_NUM - 1 < y_page)
        y_page = OLED_PAGE_MAX_NUM - 1;             // 页地址不能超过最大值
    if (0 == len)
        return ;    // 如果是空字符串，则直接返回

    // 依次显示每个字符
    while ('\0' != *en_num_str)
    {
        ch = *en_num_str++;     // 取一个字符
        oled_show_en_num_char(x_column, y_page, ch);    // 在指定位置显示指定的字符
        x_column += 8;          // 移动到下一个位置
    }

    return ;
}


/*
 * 在oled的指定坐标上显示一个指定的汉字
 * @x_column 坐标x，列地址
 * @y_page 坐标y，页地址
 * @code 待显示的汉字的字模编码
 */
void oled_show_cn_word(u8 x_column, u8 y_page, const unsigned char *code)
{
    u8 col = 0;
    
    // 显示第一个page
    oled_set_pos(x_column, y_page);         // 设置起始坐标
    for (col = 0; col < 16; col++)          // 16x16的汉字，宽度为16
    {
        oled_i2c_write_data(code[col]);
    }

    // 显示第二个page
    oled_set_pos(x_column, y_page + 1);     // 设置起始坐标
    for (col = 0; col < 16; col++)
    {
        oled_i2c_write_data(code[16 + col]);    // 注意，取模时，一个汉字的32个数据，放在一行中
    }

    return ;
}


/*
 * 在oled上显示一个中文字符串
 * @x_column 字符串在oled上的起始坐标x，列地址
 * @y_page 字符串在oled上的起始坐标y，页地址
 */
void oled_show_cn_test_str(u8 x_column, u8 y_page)
{
    u8 num = 0;

    // 依次显示数组F16x16_cn_test_str中的每个汉字
    for (num = 0; num < 6; num++)      // 中文字符串“作者：勤为本”，共6个中文汉字
    {
        // 显示一个汉字
        oled_show_cn_word(x_column, y_page, F16x16_cn_test_str[num]);
        x_column += 16;                 // 16x16的汉字
    }

    return ;
}


// oled清屏
void oled_clear_screen(void)
{
    oled_fill(0x00);
}


// oled休眠--休眠模式下，功耗不到10uA
void oled_sleep(void)
{
    // 关闭电荷泵
    oled_i2c_write_cmd(0x8D);
    oled_i2c_write_cmd(0x10);

    // display OFF
    oled_i2c_write_cmd(0xAE);

    return ;
}


// 唤醒oled，正常工作模式
void oled_normal_work(void)
{
    // 开启电荷泵
    oled_i2c_write_cmd(0x8D);
    oled_i2c_write_cmd(0x14);

    // display ON
    oled_i2c_write_cmd(0xAF);

    return ;
}

